其他
利用人工智能给游戏“开挂”,但求一败
PaddleX是百度飞桨推出的深度学习全流程开发工具,本文以微信小游戏跳一跳为例,体验了PaddleX从数据准备到模型部署的全流程。
第一步:理解跳一跳
在跳一跳游戏中,玩家通过按下手机屏幕来控制小人跳起,若准确落入下一个跳台则积分,否则游戏失败。主要有这几个界面:
第二步:制作数据
第三步:模型训练
matplotlib.use('Agg')
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
import paddlex as pdx
#把制作好的数据集解压到相应子目录
!rm data/game -r
# !cat game.zip.* > game.zip
!unzip game.zip
!mv game data/
# !rm game.zip
import zipfile
import xml.etree.ElementTree as ET
import re
import numpy as np
lables = os.listdir("data/game/lable")
print("lables:",len(lables))
trains = os.listdir("data/game/train1")
print("trains:",len(trains))
_lable = []
ratio = 0.8
offset = int(len(lables)*ratio)
np.random.shuffle(lables)
path = "data/game/"
with open(path + "train_list.txt","w") as f:
for lable in lables[:offset]:
if lable.split(".")[0] + ".jpg" in trains:
f.writelines("train1/" + lable.split(".")[0] + ".jpg" + " " + "lable/" + lable +"\n")
tree = ET.parse(path + "lable/" + lable)
root = tree.getroot()
objs = root.findall("object")
for obj in objs:
if obj.find("name").text not in _lable:
_lable.append(obj.find("name").text)
tree.find("path").text = path + "lable/" + lable
tree.write(path + "lable/" + lable)
with open(path + "test_list.txt","w") as f:
for lable in lables[offset:]:
if lable.split(".")[0] + ".jpg" in trains:
f.writelines("train1/" + lable.split(".")[0] + ".jpg" + " " + "lable/" + lable +"\n")
tree = ET.parse(path + "lable/" + lable)
root = tree.getroot()
objs = root.findall("object")
for obj in objs:
tree.find("path").text = path + "lable/" + lable
tree.write(path + "lable/" + lable)
if obj.find("name").text not in _lable:
_lable.append(obj.find("name").text)
with open(path + "val_list.txt","w") as f:
for lable in lables[offset:]:
if lable.split(".")[0] + ".jpg" in trains:
f.writelines("train1/" + lable.split(".")[0] + ".jpg" + " " + "lable/" + lable +"\n")
print(_lable)
with open(path + "lable.txt","w") as f:
for lable in _lable:
f.writelines(lable+"\n")
train_transforms = transforms.Compose([
transforms.MixupImage(mixup_epoch=250),
transforms.RandomDistort(),
transforms.RandomExpand(),
transforms.RandomCrop(),
transforms.Resize(target_size=608, interp='RANDOM'),
transforms.RandomHorizontalFlip(),
transforms.Normalize(),
])
eval_transforms = transforms.Compose([
transforms.Resize(target_size=608, interp='CUBIC'),
transforms.Normalize(),
])
data_dir='data/game',
file_list='data/game/train_list.txt',
label_list='data/game/lable.txt',
transforms=train_transforms,
shuffle=True)
eval_dataset = pdx.datasets.VOCDetection(
data_dir='data/game',
file_list='data/game/val_list.txt',
label_list='data/game/lable.txt',
transforms=eval_transforms)
model = pdx.det.YOLOv3(num_classes=num_classes, backbone='DarkNet53')
model.train(
num_epochs=300,
train_dataset=train_dataset,
train_batch_size=12,
eval_dataset=eval_dataset,
learning_rate=0.000125,
lr_decay_epochs=[210, 240],
save_interval_epochs=20,
save_dir='output/yolov3_darknet53',
use_vdl=True)
# 训练完了,就可以用来检测小人与下一个跳台分别在什么地方了。
# 设置使用0号GPU卡(如无GPU,执行此代码后会使用CPU训练模型)
import matplotlib
matplotlib.use('Agg')
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
import paddlex as pdx
# 加载模型文件夹路径,这里修改为训练时模型保存的路径:
base_path = "output/best_model"
model = pdx.load_model(base_path)
第四步:获取屏幕画面
import win32api
from PIL import ImageGrab
import cv2
import time
import numpy as np
classname = None
titlename = "360手机演示"
#获取句柄
hwnd = win32gui.FindWindow(classname, titlename)
print("获取窗口:",hwnd)
#获取窗口左上角和右下角坐标
left, top, right, bottom = win32gui.GetWindowRect(hwnd)
print(left, top, right, bottom)
def getScreen():
n = time.time()
left, top, right, bottom = win32gui.GetWindowRect(hwnd)
img = ImageGrab.grab(bbox=(left, top, right, bottom))
img = np.array(img.getdata(), np.uint8).reshape(img.size[1], img.size[0], 3)
#img = img[...,::-1]
cv2.imwrite("response.jpg", img)
getScreen()
第五步:目标检测
result = model.predict("temp/" +filename)
pdx.det.visualize("temp/" +filename, result, threshold=0.5, save_dir='./output/yolov3_mobilenetv1')
#result的格式是
# [{'category_id': 1,
# 'bbox': [164.57411193847656, 248.15550231933594, 34.76348876953125, 16.626205444335938],
# 'score': 0.972798228263855,
# 'category': 'next'}]
第六步:估算按屏时间
#计算距离d和时间t的之间函数关系,
#假定小人的路径是抛物线,与发射角和时间都相关
#当按下时间为0时,发射方向为垂直向上的,此时发射角为0度
#按下时间越长,发射角越接近0度,所以构造以下函数
#发射角aa = Pi/2 - Pi/2*(a/(c*t + 1.0) + (1-a))
#发射起始速度V与按下时间成正比,V = z*t
#Vx = V*cos(aa),Vy= V*sin(aa)
#空中飞行时间为T = 2*Vy/g
#跳跃距离为Vx*T = V*cos(aa)* 2*V*sin(aa)/g= z^2 * t^2 *sin(aa)*cos(aa) /g =b *t^2 * sin(2*aa)
#
#这部分其实应该用深度学习来找这个函数,这个以后再弄
import math
def d2t(a,b,c,d):
#返回按下的时间长度,感觉是单调函数,所以二分法试一下
t0,t1 = 0.0,500.0
for _ in range(100):
t = t1/2.0 + t0/2.0
aa = (3.1415926*(a/(c*t+1.0)+1-a) - 3.1415926/2)
d0 = b * t**2 *math.sin(aa)
if (d - d0)**2 < 0.000001:
return t
if d > d0:
t0 = t1/2.0 + t0/2.0
else:
t1 = t1/2.0 + t0/2.0
return t
#手工调试这组参数
a,b,c = 0.25, 420, 3.0
#最后就是在屏幕上对游戏进行操作:
#需要用到autopy库
!pip install autopy
import autopy
def toggle(x,y,t):
#在x,y位置按下t时长,模拟在手机上的按下操作
autopy.mouse.move(x ,y)
autopy.mouse.toggle(None,True)
time.sleep(t)
autopy.mouse.toggle(None,False)
第七步:总结